package com.yupi.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.common.ErrorCode;
import com.yupi.common.ResultUtils;
import com.yupi.constant.UserConstant;
import com.yupi.exception.BusinessException;
import com.yupi.mapper.UserinfoMapper;
import com.yupi.model.domain.Userinfo;
import com.yupi.service.UserinfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author 木兔光太郎
 * @description 针对表【userinfo(用户信息)】的数据库操作Service实现
 * @createDate 2024-07-29 13:50:37
 */
@Service
@Slf4j
public class UserinfoServiceImpl extends ServiceImpl<UserinfoMapper, Userinfo> implements UserinfoService {
    //TODO 将null和-1修改为抛出的异常类

    //盐的值
    private static final String SALT = "x12fgzha";


    @Resource
    private UserinfoMapper userinfoMapper;

    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public Userinfo userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        //1.判空校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //2.校验位数合法性
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账户长度<4");
        }
        if (userPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度<8");
        }
        //3.验证账号是否包含特殊字符
        String regex = "[[ _`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]|\\n|\\r|\\t]+";
        Matcher matcher = Pattern.compile(regex).matcher(userAccount);
        if (matcher.find()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账户包含特殊字符");
        }
        //4.账户是否已存在/密码是否正确
        String saltPasswrod = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        QueryWrapper<Userinfo> qw = new QueryWrapper<>();
        qw.eq("user_account", userAccount);
        qw.eq("user_password", saltPasswrod);
        qw.eq("user_status", 0);
        qw.eq("is_deleted", 0);
        Userinfo userinfo = this.userinfoMapper.selectOne(qw);
        if (userinfo == null) {
            if (log.isInfoEnabled()) {
                log.info("user login failed,userAccount or userPassword error");
            }
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "user login failed,userAccount or userPassword error");
        }
        //校验全部通过，返回用户信息(脱敏)
        Userinfo safetyUser = getSafetyUser(userinfo);
        request.getSession().setAttribute(UserConstant.USER_LOGIN_STATUS, safetyUser);

        return safetyUser;
    }

    /**
     * @param userAccount   账号
     * @param userPassword  密码
     * @param checkPassword 确认密码
     * @return
     */
    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword, String planetCode) {
        //1.判空校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword, planetCode)) {
            return -1;
        }
        //2.校验位数合法性
        if (userAccount.length() < 4) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账户长度<4");
        }
        if (planetCode.length() > 5 || planetCode.length() < 1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "星球编号长度不符");
        }
        if (userPassword.length() < 8 || checkPassword.length() < 8) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码长度<8");
        }

        //3.两次密码是否相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次密码不一致");
        }
        //4.验证账号是否包含特殊字符
        String regex = "[[ _`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~！@#￥%……&*（）——+|{}【】‘；：”“’。，、？]|\\n|\\r|\\t]+";
        Matcher matcher = Pattern.compile(regex).matcher(userAccount);
        if (matcher.find()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号包含特殊字符");
        }
        //5.账户是否已存在
        QueryWrapper<Userinfo> qw = new QueryWrapper<>();
        qw.eq("user_account", userAccount);
        long count = this.userinfoMapper.selectCount(qw);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "该账号已存在");
        }
        //6.星球编号不能重复
        QueryWrapper<Userinfo> qw2 = new QueryWrapper<>();
        qw.eq("planet_code", planetCode);
        count = this.userinfoMapper.selectCount(qw);
        if (count > 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "星球编号重复");
        }
        //MD5加密密码
        String saltPasswrod = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        //插入数据
        Userinfo userinfo = new Userinfo();
        userinfo.setUserAccount(userAccount);
        userinfo.setPlanetCode(planetCode);
        userinfo.setUserPassword(saltPasswrod);
        int result = this.userinfoMapper.insert(userinfo);
        if (result <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "注册用户失败");
        }
        return userinfo.getId();
    }

    @Override
    public int userLogout(HttpServletRequest request) {
        request.getSession().removeAttribute(UserConstant.USER_LOGIN_STATUS);
        return 1;
    }

    //用户脱敏方法
    @Override
    public Userinfo getSafetyUser(Userinfo userinfo) {
        if (userinfo == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        Userinfo safetyUser = new Userinfo();
        safetyUser.setId(userinfo.getId());
        safetyUser.setUsername(userinfo.getUsername());
        safetyUser.setAvatarUrl(userinfo.getAvatarUrl());
        safetyUser.setGender(userinfo.getGender());
        safetyUser.setProfile(userinfo.getProfile());
        safetyUser.setPlanetCode(userinfo.getPlanetCode());
        safetyUser.setUserAccount(userinfo.getUserAccount());
        safetyUser.setEmail(userinfo.getEmail());
        safetyUser.setUserStatus(userinfo.getUserStatus());
        safetyUser.setUserRole(userinfo.getUserRole());
        safetyUser.setTags(userinfo.getTags());
        return safetyUser;
    }


    @Override
    public List<Userinfo> searchUserByTags(List<String> tags) {
        if (CollectionUtils.isEmpty(tags)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "标签为空");
        }
        QueryWrapper<Userinfo> qw = new QueryWrapper();
        //查询
        for (String tagName : tags) {
            qw.like("tags", tagName);
        }
        List<Userinfo> userListByTag = userinfoMapper.selectList(qw);
        //脱敏后的数组
        List<Userinfo> safetyUserList = userListByTag.stream().map(this::getSafetyUser).collect(Collectors.toList());

        return safetyUserList;
    }

    @Override
    public Integer updateUser(Userinfo userinfo, Userinfo loginUser) {
        //如果当前登录用户是管理员，它可以修改任意用户的信息
        //如果当前登录用户不是管理员，他只能修改自己的信息
        //如果当前登录用户修改的信息既不是自己的，并且也不是管理员，则抛出权限不足异常
        if (!Objects.equals(loginUser.getId(), userinfo.getId()) && !isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        //鉴权  仅管理员和本人可以可以操作
        Long id = userinfo.getId();
        //判断当前要修改的对象是否为空
        if (id == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //要修改的对象
        Userinfo user = this.userinfoMapper.selectById(id);
        if (user == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }


        return userinfoMapper.updateById(userinfo);
    }

    @Override
    public Userinfo getLoginUser(HttpServletRequest request) {
        if (request == null) {
            return null;
        }
        Object object = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATUS);
        return (Userinfo) object;
    }

    @Override
    //判断是否为管理员方法
    public boolean isAdmin(HttpServletRequest request) {
        Object object = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATUS);
        if (object == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Userinfo userinfo = (Userinfo) object;
        return UserConstant.ADMIN_ROLE.equals(userinfo.getUserRole()) && userinfo != null;
    }

    @Override
    //判断是否为管理员方法的方法重载
    public boolean isAdmin(Userinfo loginUser) {
        return UserConstant.ADMIN_ROLE.equals(loginUser.getUserRole()) && loginUser != null;
    }

    @Override
    public Page<Userinfo> recommendUsers(long pageNum, long pageSize, HttpServletRequest request) {
        Userinfo loginUser = getLoginUser(request);
        //如果有缓存，直接从缓存中读取
        String redisKey = String.format("dogfriend:user:recommend:%s", loginUser.getId());
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        Page<Userinfo> uesrPage = (Page<Userinfo>) valueOperations.get(redisKey);
        if (uesrPage != null) {
            return uesrPage;
        }
        //没有缓存，从数据库中查询，然后将结果存入缓存
        QueryWrapper<Userinfo> qw = new QueryWrapper<>();
        //计算页码公式
        uesrPage = this.page(new Page<>(pageNum, pageSize), qw);
        //如果出现异常，捕获到，也能正常返回数据
        try {
            valueOperations.set(redisKey, uesrPage, 100, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("redis set key error", e);
        }
        return uesrPage;
    }
}




